#!/bin/bash

# 统一包管理接口模块
# 支持 apt、yum、dnf 等不同包管理器的统一接口
# 实现智能镜像源选择、网络连接优化、离线安装支持

# =============================================================================
# 全局变量和配置
# =============================================================================

# 包管理器类型
PKG_MANAGER=""
PKG_MANAGER_TYPE=""

# 镜像源配置
MIRROR_SOURCES=()
CURRENT_MIRROR=""
MIRROR_TEST_TIMEOUT=5

# 离线安装配置
OFFLINE_MODE=false
OFFLINE_CACHE_DIR="/tmp/pkg-cache"
PACKAGE_CACHE_DIR=""

# 依赖版本锁定
VERSION_LOCK_FILE=""
LOCKED_PACKAGES=()

# =============================================================================
# 包管理器检测和初始化
# =============================================================================

# 检测包管理器类型
detect_package_manager() {
    if command -v apt-get >/dev/null 2>&1; then
        PKG_MANAGER="apt-get"
        PKG_MANAGER_TYPE="debian"
        PACKAGE_CACHE_DIR="/var/cache/apt/archives"
    elif command -v dnf >/dev/null 2>&1; then
        PKG_MANAGER="dnf"
        PKG_MANAGER_TYPE="redhat"
        PACKAGE_CACHE_DIR="/var/cache/dnf"
    elif command -v yum >/dev/null 2>&1; then
        PKG_MANAGER="yum"
        PKG_MANAGER_TYPE="redhat"
        PACKAGE_CACHE_DIR="/var/cache/yum"
    elif command -v zypper >/dev/null 2>&1; then
        PKG_MANAGER="zypper"
        PKG_MANAGER_TYPE="suse"
        PACKAGE_CACHE_DIR="/var/cache/zypp"
    elif command -v pacman >/dev/null 2>&1; then
        PKG_MANAGER="pacman"
        PKG_MANAGER_TYPE="arch"
        PACKAGE_CACHE_DIR="/var/cache/pacman/pkg"
    else
        echo "错误: 未检测到支持的包管理器"
        return 1
    fi
    
    echo "检测到包管理器: $PKG_MANAGER ($PKG_MANAGER_TYPE)"
    return 0
}

# 初始化包管理器配置
init_package_manager() {
    detect_package_manager || return 1
    
    # 设置版本锁定文件
    VERSION_LOCK_FILE="/etc/pkg-version-lock.conf"
    
    # 创建离线缓存目录
    mkdir -p "$OFFLINE_CACHE_DIR"
    
    # 加载版本锁定配置
    load_version_locks
    
    # 初始化镜像源列表
    init_mirror_sources
    
    return 0
}

# =============================================================================
# 镜像源管理
# =============================================================================

# 初始化镜像源列表
init_mirror_sources() {
    case "$PKG_MANAGER_TYPE" in
        debian)
            MIRROR_SOURCES=(
                "https://mirrors.aliyun.com/ubuntu/"
                "https://mirrors.tuna.tsinghua.edu.cn/ubuntu/"
                "https://mirrors.ustc.edu.cn/ubuntu/"
                "https://mirrors.163.com/ubuntu/"
                "http://archive.ubuntu.com/ubuntu/"
            )
            ;;
        redhat)
            MIRROR_SOURCES=(
                "https://mirrors.aliyun.com/centos/"
                "https://mirrors.tuna.tsinghua.edu.cn/centos/"
                "https://mirrors.ustc.edu.cn/centos/"
                "https://mirrors.163.com/centos/"
                "http://mirror.centos.org/centos/"
            )
            ;;
        suse)
            MIRROR_SOURCES=(
                "https://mirrors.aliyun.com/opensuse/"
                "https://mirrors.tuna.tsinghua.edu.cn/opensuse/"
                "https://download.opensuse.org/"
            )
            ;;
        arch)
            MIRROR_SOURCES=(
                "https://mirrors.aliyun.com/archlinux/"
                "https://mirrors.tuna.tsinghua.edu.cn/archlinux/"
                "https://mirrors.ustc.edu.cn/archlinux/"
            )
            ;;
    esac
}

# 测试镜像源连接速度
test_mirror_speed() {
    local mirror_url="$1"
    local test_file=""
    
    case "$PKG_MANAGER_TYPE" in
        debian)
            test_file="ls-lR.gz"
            ;;
        redhat)
            test_file="repodata/repomd.xml"
            ;;
        *)
            test_file=""
            ;;
    esac
    
    if [[ -n "$test_file" ]]; then
        local test_url="${mirror_url%/}/$test_file"
        local start_time=$(date +%s%N)
        
        if curl -s --connect-timeout "$MIRROR_TEST_TIMEOUT" --max-time "$MIRROR_TEST_TIMEOUT" \
               -o /dev/null "$test_url" 2>/dev/null; then
            local end_time=$(date +%s%N)
            local duration=$(( (end_time - start_time) / 1000000 ))  # 转换为毫秒
            echo "$duration"
            return 0
        fi
    fi
    
    echo "999999"  # 连接失败返回最大值
    return 1
}

# 选择最快的镜像源
select_fastest_mirror() {
    echo "正在测试镜像源速度..."
    
    local fastest_mirror=""
    local fastest_time=999999
    
    for mirror in "${MIRROR_SOURCES[@]}"; do
        echo "测试镜像源: $mirror"
        local response_time=$(test_mirror_speed "$mirror")
        
        if [[ $response_time -lt $fastest_time ]]; then
            fastest_time=$response_time
            fastest_mirror="$mirror"
        fi
        
        echo "响应时间: ${response_time}ms"
    done
    
    if [[ -n "$fastest_mirror" ]]; then
        CURRENT_MIRROR="$fastest_mirror"
        echo "选择最快镜像源: $fastest_mirror (${fastest_time}ms)"
        return 0
    else
        echo "警告: 无法连接到任何镜像源，使用默认源"
        return 1
    fi
}

# 配置镜像源
configure_mirror() {
    local mirror_url="$1"
    
    if [[ -z "$mirror_url" ]]; then
        select_fastest_mirror
        mirror_url="$CURRENT_MIRROR"
    fi
    
    if [[ -z "$mirror_url" ]]; then
        echo "警告: 未指定镜像源，跳过配置"
        return 1
    fi
    
    echo "配置镜像源: $mirror_url"
    
    case "$PKG_MANAGER_TYPE" in
        debian)
            configure_apt_mirror "$mirror_url"
            ;;
        redhat)
            configure_yum_mirror "$mirror_url"
            ;;
        suse)
            configure_zypper_mirror "$mirror_url"
            ;;
        arch)
            configure_pacman_mirror "$mirror_url"
            ;;
        *)
            echo "错误: 不支持的包管理器类型: $PKG_MANAGER_TYPE"
            return 1
            ;;
    esac
}

# 配置APT镜像源
configure_apt_mirror() {
    local mirror_url="$1"
    local sources_file="/etc/apt/sources.list"
    local backup_file="${sources_file}.backup.$(date +%Y%m%d-%H%M%S)"
    
    # 备份原文件
    cp "$sources_file" "$backup_file"
    
    # 检测Ubuntu版本
    local ubuntu_version=""
    if [[ -f /etc/lsb-release ]]; then
        ubuntu_version=$(grep DISTRIB_CODENAME /etc/lsb-release | cut -d= -f2)
    fi
    
    if [[ -z "$ubuntu_version" ]]; then
        ubuntu_version="focal"  # 默认版本
    fi
    
    # 生成新的sources.list
    cat > "$sources_file" << EOF
# 自动生成的APT镜像源配置
# 生成时间: $(date)
# 备份文件: $backup_file

deb $mirror_url $ubuntu_version main restricted universe multiverse
deb $mirror_url $ubuntu_version-updates main restricted universe multiverse
deb $mirror_url $ubuntu_version-backports main restricted universe multiverse
deb $mirror_url $ubuntu_version-security main restricted universe multiverse

# 源码包（可选）
# deb-src $mirror_url $ubuntu_version main restricted universe multiverse
# deb-src $mirror_url $ubuntu_version-updates main restricted universe multiverse
EOF
    
    echo "APT镜像源配置完成"
    return 0
}

# 配置YUM/DNF镜像源
configure_yum_mirror() {
    local mirror_url="$1"
    local repo_dir="/etc/yum.repos.d"
    local backup_dir="${repo_dir}.backup.$(date +%Y%m%d-%H%M%S)"
    
    # 备份原配置
    cp -r "$repo_dir" "$backup_dir"
    
    # 禁用原有仓库
    find "$repo_dir" -name "*.repo" -exec sed -i 's/enabled=1/enabled=0/g' {} \;
    
    # 创建新的仓库配置
    cat > "$repo_dir/custom-mirror.repo" << EOF
# 自动生成的YUM/DNF镜像源配置
# 生成时间: $(date)
# 备份目录: $backup_dir

[base]
name=CentOS Base
baseurl=${mirror_url%/}/\$releasever/os/\$basearch/
gpgcheck=1
enabled=1

[updates]
name=CentOS Updates
baseurl=${mirror_url%/}/\$releasever/updates/\$basearch/
gpgcheck=1
enabled=1

[extras]
name=CentOS Extras
baseurl=${mirror_url%/}/\$releasever/extras/\$basearch/
gpgcheck=1
enabled=1
EOF
    
    echo "YUM/DNF镜像源配置完成"
    return 0
}

# 配置Zypper镜像源
configure_zypper_mirror() {
    local mirror_url="$1"
    
    # 添加镜像源
    zypper ar -f "$mirror_url" custom-mirror
    
    # 刷新仓库
    zypper refresh
    
    echo "Zypper镜像源配置完成"
    return 0
}

# 配置Pacman镜像源
configure_pacman_mirror() {
    local mirror_url="$1"
    local mirrorlist="/etc/pacman.d/mirrorlist"
    local backup_file="${mirrorlist}.backup.$(date +%Y%m%d-%H%M%S)"
    
    # 备份原文件
    cp "$mirrorlist" "$backup_file"
    
    # 添加镜像源到文件顶部
    sed -i "1i Server = $mirror_url\$repo/os/\$arch" "$mirrorlist"
    
    echo "Pacman镜像源配置完成"
    return 0
}

# =============================================================================
# 版本锁定管理
# =============================================================================

# 加载版本锁定配置
load_version_locks() {
    LOCKED_PACKAGES=()
    
    if [[ -f "$VERSION_LOCK_FILE" ]]; then
        while IFS= read -r line; do
            # 跳过注释和空行
            [[ "$line" =~ ^[[:space:]]*# ]] && continue
            [[ -z "${line// }" ]] && continue
            
            LOCKED_PACKAGES+=("$line")
        done < "$VERSION_LOCK_FILE"
        
        echo "加载了 ${#LOCKED_PACKAGES[@]} 个版本锁定规则"
    fi
}

# 保存版本锁定配置
save_version_locks() {
    cat > "$VERSION_LOCK_FILE" << EOF
# 包版本锁定配置文件
# 格式: package_name=version
# 生成时间: $(date)

EOF
    
    for lock in "${LOCKED_PACKAGES[@]}"; do
        echo "$lock" >> "$VERSION_LOCK_FILE"
    done
    
    echo "版本锁定配置已保存到: $VERSION_LOCK_FILE"
}

# 添加版本锁定
add_version_lock() {
    local package="$1"
    local version="$2"
    
    if [[ -z "$package" || -z "$version" ]]; then
        echo "错误: 包名和版本不能为空"
        return 1
    fi
    
    local lock_entry="${package}=${version}"
    
    # 检查是否已存在
    for existing_lock in "${LOCKED_PACKAGES[@]}"; do
        if [[ "$existing_lock" =~ ^${package}= ]]; then
            echo "更新版本锁定: $lock_entry"
            # 替换现有锁定
            LOCKED_PACKAGES=("${LOCKED_PACKAGES[@]/$existing_lock/$lock_entry}")
            save_version_locks
            return 0
        fi
    done
    
    # 添加新锁定
    LOCKED_PACKAGES+=("$lock_entry")
    echo "添加版本锁定: $lock_entry"
    save_version_locks
    return 0
}

# 移除版本锁定
remove_version_lock() {
    local package="$1"
    
    if [[ -z "$package" ]]; then
        echo "错误: 包名不能为空"
        return 1
    fi
    
    local new_locks=()
    local removed=false
    
    for lock in "${LOCKED_PACKAGES[@]}"; do
        if [[ ! "$lock" =~ ^${package}= ]]; then
            new_locks+=("$lock")
        else
            removed=true
            echo "移除版本锁定: $lock"
        fi
    done
    
    if [[ "$removed" == "true" ]]; then
        LOCKED_PACKAGES=("${new_locks[@]}")
        save_version_locks
        return 0
    else
        echo "警告: 未找到包 $package 的版本锁定"
        return 1
    fi
}

# 检查包是否被锁定
is_package_locked() {
    local package="$1"
    
    for lock in "${LOCKED_PACKAGES[@]}"; do
        if [[ "$lock" =~ ^${package}= ]]; then
            echo "${lock#*=}"  # 返回锁定的版本
            return 0
        fi
    done
    
    return 1
}

# =============================================================================
# 离线安装支持
# =============================================================================

# 启用离线模式
enable_offline_mode() {
    OFFLINE_MODE=true
    echo "已启用离线安装模式"
    echo "缓存目录: $OFFLINE_CACHE_DIR"
}

# 禁用离线模式
disable_offline_mode() {
    OFFLINE_MODE=false
    echo "已禁用离线安装模式"
}

# 下载包到缓存
download_package() {
    local package="$1"
    local version="$2"
    
    echo "下载包到缓存: $package${version:+=$version}"
    
    case "$PKG_MANAGER_TYPE" in
        debian)
            if [[ -n "$version" ]]; then
                apt-get download "${package}=${version}" -o Dir::Cache::Archives="$OFFLINE_CACHE_DIR"
            else
                apt-get download "$package" -o Dir::Cache::Archives="$OFFLINE_CACHE_DIR"
            fi
            ;;
        redhat)
            if [[ -n "$version" ]]; then
                $PKG_MANAGER download "${package}-${version}" --downloaddir="$OFFLINE_CACHE_DIR"
            else
                $PKG_MANAGER download "$package" --downloaddir="$OFFLINE_CACHE_DIR"
            fi
            ;;
        *)
            echo "错误: 离线下载暂不支持 $PKG_MANAGER_TYPE"
            return 1
            ;;
    esac
}

# 从缓存安装包
install_from_cache() {
    local package="$1"
    
    echo "从缓存安装包: $package"
    
    case "$PKG_MANAGER_TYPE" in
        debian)
            local deb_files=$(find "$OFFLINE_CACHE_DIR" -name "${package}*.deb" 2>/dev/null)
            if [[ -n "$deb_files" ]]; then
                dpkg -i $deb_files
                apt-get install -f  # 修复依赖
            else
                echo "错误: 缓存中未找到包 $package"
                return 1
            fi
            ;;
        redhat)
            local rpm_files=$(find "$OFFLINE_CACHE_DIR" -name "${package}*.rpm" 2>/dev/null)
            if [[ -n "$rpm_files" ]]; then
                rpm -ivh $rpm_files
            else
                echo "错误: 缓存中未找到包 $package"
                return 1
            fi
            ;;
        *)
            echo "错误: 离线安装暂不支持 $PKG_MANAGER_TYPE"
            return 1
            ;;
    esac
}

# 清理缓存
clean_cache() {
    echo "清理包缓存..."
    
    if [[ -d "$OFFLINE_CACHE_DIR" ]]; then
        rm -rf "$OFFLINE_CACHE_DIR"/*
        echo "离线缓存已清理"
    fi
    
    case "$PKG_MANAGER_TYPE" in
        debian)
            apt-get clean
            ;;
        redhat)
            $PKG_MANAGER clean all
            ;;
        suse)
            zypper clean
            ;;
        arch)
            pacman -Sc --noconfirm
            ;;
    esac
    
    echo "系统缓存已清理"
}

# =============================================================================
# 统一包管理接口
# =============================================================================

# 更新包列表
pkg_update() {
    echo "更新包列表..."
    
    case "$PKG_MANAGER_TYPE" in
        debian)
            apt-get update
            ;;
        redhat)
            $PKG_MANAGER makecache
            ;;
        suse)
            zypper refresh
            ;;
        arch)
            pacman -Sy
            ;;
        *)
            echo "错误: 不支持的包管理器类型: $PKG_MANAGER_TYPE"
            return 1
            ;;
    esac
}

# 安装包
pkg_install() {
    local packages=("$@")
    
    if [[ ${#packages[@]} -eq 0 ]]; then
        echo "错误: 未指定要安装的包"
        return 1
    fi
    
    echo "安装包: ${packages[*]}"
    
    # 检查离线模式
    if [[ "$OFFLINE_MODE" == "true" ]]; then
        for package in "${packages[@]}"; do
            install_from_cache "$package"
        done
        return $?
    fi
    
    # 处理版本锁定
    local install_packages=()
    for package in "${packages[@]}"; do
        local locked_version=$(is_package_locked "$package")
        if [[ $? -eq 0 ]]; then
            echo "使用锁定版本: ${package}=${locked_version}"
            install_packages+=("${package}=${locked_version}")
        else
            install_packages+=("$package")
        fi
    done
    
    case "$PKG_MANAGER_TYPE" in
        debian)
            apt-get install -y "${install_packages[@]}"
            ;;
        redhat)
            $PKG_MANAGER install -y "${install_packages[@]}"
            ;;
        suse)
            zypper install -y "${install_packages[@]}"
            ;;
        arch)
            pacman -S --noconfirm "${install_packages[@]}"
            ;;
        *)
            echo "错误: 不支持的包管理器类型: $PKG_MANAGER_TYPE"
            return 1
            ;;
    esac
}

# 卸载包
pkg_remove() {
    local packages=("$@")
    
    if [[ ${#packages[@]} -eq 0 ]]; then
        echo "错误: 未指定要卸载的包"
        return 1
    fi
    
    echo "卸载包: ${packages[*]}"
    
    case "$PKG_MANAGER_TYPE" in
        debian)
            apt-get remove -y "${packages[@]}"
            ;;
        redhat)
            $PKG_MANAGER remove -y "${packages[@]}"
            ;;
        suse)
            zypper remove -y "${packages[@]}"
            ;;
        arch)
            pacman -R --noconfirm "${packages[@]}"
            ;;
        *)
            echo "错误: 不支持的包管理器类型: $PKG_MANAGER_TYPE"
            return 1
            ;;
    esac
}

# 搜索包
pkg_search() {
    local keyword="$1"
    
    if [[ -z "$keyword" ]]; then
        echo "错误: 未指定搜索关键词"
        return 1
    fi
    
    echo "搜索包: $keyword"
    
    case "$PKG_MANAGER_TYPE" in
        debian)
            apt-cache search "$keyword"
            ;;
        redhat)
            $PKG_MANAGER search "$keyword"
            ;;
        suse)
            zypper search "$keyword"
            ;;
        arch)
            pacman -Ss "$keyword"
            ;;
        *)
            echo "错误: 不支持的包管理器类型: $PKG_MANAGER_TYPE"
            return 1
            ;;
    esac
}

# 显示包信息
pkg_info() {
    local package="$1"
    
    if [[ -z "$package" ]]; then
        echo "错误: 未指定包名"
        return 1
    fi
    
    echo "包信息: $package"
    
    case "$PKG_MANAGER_TYPE" in
        debian)
            apt-cache show "$package"
            ;;
        redhat)
            $PKG_MANAGER info "$package"
            ;;
        suse)
            zypper info "$package"
            ;;
        arch)
            pacman -Si "$package"
            ;;
        *)
            echo "错误: 不支持的包管理器类型: $PKG_MANAGER_TYPE"
            return 1
            ;;
    esac
}

# 列出已安装的包
pkg_list() {
    echo "已安装的包:"
    
    case "$PKG_MANAGER_TYPE" in
        debian)
            dpkg -l | grep ^ii
            ;;
        redhat)
            $PKG_MANAGER list installed
            ;;
        suse)
            zypper search --installed-only
            ;;
        arch)
            pacman -Q
            ;;
        *)
            echo "错误: 不支持的包管理器类型: $PKG_MANAGER_TYPE"
            return 1
            ;;
    esac
}

# 检查包是否已安装
pkg_is_installed() {
    local package="$1"
    
    if [[ -z "$package" ]]; then
        return 1
    fi
    
    case "$PKG_MANAGER_TYPE" in
        debian)
            dpkg -l "$package" 2>/dev/null | grep -q ^ii
            ;;
        redhat)
            rpm -q "$package" >/dev/null 2>&1
            ;;
        suse)
            zypper search --installed-only "$package" | grep -q "$package"
            ;;
        arch)
            pacman -Q "$package" >/dev/null 2>&1
            ;;
        *)
            return 1
            ;;
    esac
}

# 升级所有包
pkg_upgrade() {
    echo "升级所有包..."
    
    case "$PKG_MANAGER_TYPE" in
        debian)
            apt-get upgrade -y
            ;;
        redhat)
            $PKG_MANAGER update -y
            ;;
        suse)
            zypper update -y
            ;;
        arch)
            pacman -Syu --noconfirm
            ;;
        *)
            echo "错误: 不支持的包管理器类型: $PKG_MANAGER_TYPE"
            return 1
            ;;
    esac
}

# =============================================================================
# 工具函数
# =============================================================================

# 显示包管理器状态
show_pkg_manager_status() {
    echo "包管理器状态:"
    echo "  类型: $PKG_MANAGER_TYPE"
    echo "  命令: $PKG_MANAGER"
    echo "  缓存目录: $PACKAGE_CACHE_DIR"
    echo "  离线模式: $OFFLINE_MODE"
    echo "  当前镜像: ${CURRENT_MIRROR:-未设置}"
    echo "  版本锁定: ${#LOCKED_PACKAGES[@]} 个包"
}

# 导出函数供其他脚本使用
export -f detect_package_manager
export -f init_package_manager
export -f configure_mirror
export -f select_fastest_mirror
export -f add_version_lock
export -f remove_version_lock
export -f enable_offline_mode
export -f disable_offline_mode
export -f pkg_update
export -f pkg_install
export -f pkg_remove
export -f pkg_search
export -f pkg_info
export -f pkg_list
export -f pkg_is_installed
export -f pkg_upgrade
export -f clean_cache
export -f show_pkg_manager_status